﻿using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Text;
using UnityEngine;
using Google.Protobuf;
using System;
using System.Net;

public class Launcher : MonoBehaviour
{
    Dictionary<ulong, Player> m_Players = new Dictionary<ulong, Player>();
    public static Launcher Instance;
    public uint TickCount { private set; get; }
    public float m_LastServerPing;

    private void Awake()
    {
        Instance = this;
        CommonUtil.Init();
    }

    void Start()
    {
        //IPEndPoint ip = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1234);
        //Debug.LogError(ip.Address);
        //return;


        MessageManager.Instance.Start();
        MessageDispatcher.Instance.Register<Hello>(EMessageType.RUDP_HELLO, OnHello);
        MessageDispatcher.Instance.Register<Ack>(EMessageType.RUDP_ACK, AckCallback);
        MessageDispatcher.Instance.Register<S2C_PlayerJoin>(EMessageType.RUDP_S2C_PLAYER_JOIN, OnPlayerJoin);
        MessageDispatcher.Instance.Register<S2C_PlayerSync>(EMessageType.RUDP_S2C_PLAYER_SYNC, OnPlayerSync);
        MessageDispatcher.Instance.Register<Ping>(EMessageType.RUDP_PING, OnPing);
        PendingAckManager.Instance.RegisterResendCallback(OnResend);

        MessageManager.Instance.Send(new Hello());
    }

    private void OnDestroy()
    {
        MessageDispatcher.Instance.Unregister<Ping>(EMessageType.RUDP_PING, OnPing);
        MessageDispatcher.Instance.Unregister<S2C_PlayerSync>(EMessageType.RUDP_S2C_PLAYER_SYNC, OnPlayerSync);
        MessageDispatcher.Instance.Unregister<S2C_PlayerJoin>(EMessageType.RUDP_S2C_PLAYER_JOIN, OnPlayerJoin);
        MessageDispatcher.Instance.Unregister<Ack>(EMessageType.RUDP_ACK, AckCallback);
        MessageManager.Instance.Stop();
    }

    private void Update()
    {
        PendingAckManager.Instance.Tick();
        MessageManager.Instance.Recv();

        if (CommonUtil.GetCurElapsedTime() - m_LastServerPing > 5)
        {
            MessageManager.Instance.Stop(); 
        }
    }

    private void FixedUpdate()
    {
        TickCount++;
        foreach (var item in m_Players)
        {
            item.Value.FixedTick();
        }
    }

    void AckCallback(Ack msg)
    {
        PendingAckManager.Instance.RecvAck(msg.SeqId, msg.AckBits);
    }

    void OnHello(Hello msg)
    {
        //LogManager.Log("Hello1=" + msg);
        MessageManager.Instance.Send(new Ack());
        MessageManager.Instance.Send(new C2S_PlayerJoin());
    }
    void OnPlayerJoin(S2C_PlayerJoin msg)
    {
        if (m_Players.ContainsKey(msg.UserId))
        {
            return;
        }

        LogManager.Log("create player user id=" + msg.UserId + ", pos=" + new Vector3(msg.PosX, msg.PosY, msg.PosZ));
        var ip = MessageManager.Instance.GetLocalIP();
        if (ip.Address.ToString() == msg.Host && ip.Port == msg.Port)
        {
            Facade.LocalUserID = msg.UserId;
        }
        var p = GameObject.Instantiate(Resources.Load<Player>("Player"));
        p.Init(msg.UserId, new Vector3(msg.PosX, msg.PosY, msg.PosZ));
        m_Players.Add(msg.UserId, p);

        if (p.IsLocalPlayer())
        {
            // fake
            var p1 = GameObject.Instantiate(Resources.Load<Player>("Player"));
            p1.Init(msg.UserId + 1000, new Vector3(msg.PosX, msg.PosY, msg.PosZ));
            m_Players.Add(msg.UserId + 1000, p1);
        }
    }

    void OnPlayerSync(S2C_PlayerSync msg)
    {
        if (m_Players.ContainsKey(msg.UserId))
        {
            var p = m_Players[msg.UserId];
            if (!p.IsLocalPlayer())
            {
                // TODO need lerp and judge tick count. 
                p.UpdatePos(msg.TickCount, new Vector3(msg.PosX, msg.PosY, msg.PosZ));
            }

            var p1 = m_Players[msg.UserId];
            if (p1.IsLocalPlayer())
            {
                p1 = m_Players[msg.UserId + 1000];
                // TODO need lerp and judge tick count. 
                p1.UpdatePos(msg.TickCount, new Vector3(msg.PosX, msg.PosY, msg.PosZ));
            }
        }
    }

    void OnPing(Ping msg)
    {
        m_LastServerPing = CommonUtil.GetCurElapsedTime(); 
    }

    void OnResend(uint userId, IMessage msg)
    {
        LogManager.Log("type=" + msg.GetType());
        MessageManager.Instance.Send(msg);
    }
}
